package com.xuecheng.content.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.base.exception.ResultEnum;
import com.xuecheng.base.exception.XueChengPlusException;
import com.xuecheng.content.mapper.TeachplanMapper;
import com.xuecheng.content.model.dto.AddOrUpdateTeachplanDto;
import com.xuecheng.content.model.po.Teachplan;
import com.xuecheng.content.model.po.TeachplanMedia;
import com.xuecheng.content.model.vo.TeachPlanTreeVo;
import com.xuecheng.content.service.TeachplanMediaService;
import com.xuecheng.content.service.TeachplanService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 课程计划 服务实现类
 * </p>
 *
 * @author itcast
 */
@Slf4j
@Service
public class TeachplanServiceImpl extends ServiceImpl<TeachplanMapper, Teachplan> implements TeachplanService {


    @Resource
    private TeachplanMediaService teachplanMediaService;

    @Override
    public List<TeachPlanTreeVo> getTeachPlan(Long courseId) {

        //根据课程id查询出该课程对应的全部课程大纲列表
        List<Teachplan> teachplanList = lambdaQuery().eq(Teachplan::getCourseId, courseId).list();
        //根据课程计划id查询出对应的媒体文件记录，并计入TeachPlanTreeVo对象中
        List<TeachPlanTreeVo> allTeachPlanTreeVoList = new ArrayList<>();
        if (!CollectionUtils.isEmpty(teachplanList)){
            teachplanList.forEach(teachplan -> {
                TeachplanMedia teachplanMedia = teachplanMediaService.lambdaQuery().eq(TeachplanMedia::getTeachplanId,teachplan.getId()).one();
                TeachPlanTreeVo teachPlanTreeVo = new TeachPlanTreeVo();
                BeanUtils.copyProperties(teachplan,teachPlanTreeVo);
                teachPlanTreeVo.setTeachplanMedia(teachplanMedia);
                allTeachPlanTreeVoList.add(teachPlanTreeVo);
            });
        }

        List<TeachPlanTreeVo> teachPlanTreeVoList = getChildren(0L, allTeachPlanTreeVoList);
        //根据orderBy字段进行排序
        teachPlanTreeVoList = teachPlanTreeVoList.stream().sorted(Comparator.comparing(Teachplan::getOrderby)).collect(Collectors.toList());

        teachPlanTreeVoList.forEach(teachPlanTreeVo -> {
            List<TeachPlanTreeVo> teachPlanTreeNodes = teachPlanTreeVo.getTeachPlanTreeNodes();
            if (!CollectionUtils.isEmpty(teachPlanTreeNodes)){
                teachPlanTreeNodes = teachPlanTreeNodes.stream().sorted(Comparator.comparing(Teachplan::getOrderby)).collect(Collectors.toList());
                teachPlanTreeVo.setTeachPlanTreeNodes(teachPlanTreeNodes);
            }

        });

        return teachPlanTreeVoList;


    }

    /**
     * 新增课程大纲
     * @param teachplanDto
     */
    @Override
    public void saveOrUpdateTeachPlan(AddOrUpdateTeachplanDto teachplanDto) {
        log.info("课程计划Dto:{}",teachplanDto);
        Teachplan teachplan = new Teachplan();
        BeanUtils.copyProperties(teachplanDto,teachplan);
        //根据前端是否传来id进行判断是保存还是更新
        if (teachplanDto.getId()!=null){
            //更新
            teachplan.setChangeDate(LocalDateTime.now());
            updateById(teachplan);
            return;
        }
        //保存操作
        //章节和小节的orderBy是单独计算的，也就是章节是1,2,3....  小节也是1,2,3....
        teachplan.setCreateDate(LocalDateTime.now());
        teachplan.setStatus(1);

        //根据课程id和对应的父级id查出同级的课程列表
        List<Teachplan> teachplanList = lambdaQuery().eq(Teachplan::getCourseId, teachplan.getCourseId())
                .eq(Teachplan::getParentid, teachplan.getParentid()).list();

        //orderBy字段处理
        Integer maxOrderBy = 0;
        if (!CollectionUtils.isEmpty(teachplanList)){
            //查询出orderBy最大值
            List<Integer> orderByList = teachplanList.stream().map(Teachplan::getOrderby).collect(Collectors.toList());
            maxOrderBy = Collections.max(orderByList);
            log.info("maxOrderBy:{}",maxOrderBy);
        }

        teachplan.setOrderby(maxOrderBy+1);//排序字段

        //新增大纲
        save(teachplan);

    }

    /**
     * 删除课程计划
     * @param teachplanId
     */
    @Override
    @Transactional
    public void deletePlan(Long teachplanId) {
        log.info("课程计划id:{}",teachplanId);
        //删除第一级别的大章节时要求大章节下边没有小章节时方可删除。
        //删除第二级别的小章节的同时需要将teachplan_media表关联的信息也删除。

        //判断是章节还是小节
        Long parentid = getById(teachplanId).getParentid();
        if (parentid==0){
            //章节,判断有没有小节，如果有，不能删除
            List<Teachplan> teachplanList = lambdaQuery().eq(Teachplan::getParentid, teachplanId).list();
            if (!CollectionUtils.isEmpty(teachplanList)){
                //有小节，不能删除
                throw new XueChengPlusException(ResultEnum.COURSE_PLAN_ERROR);

            }

            //如果没有小节，删除即可
            removeById(teachplanId);


        }else {
            //小节，直接删除
            removeById(teachplanId);
            //删除该计划对应的媒资信息
            teachplanMediaService
                    .remove(new LambdaQueryWrapper<TeachplanMedia>()
                            .eq(TeachplanMedia::getTeachplanId,teachplanId));
        }

    }

    /**
     * 课程计划上移操作
     * @param teachplanId
     */
    @Transactional
    @Override
    public void moveUp(Long teachplanId) {
        //上移实际是改变orderBy值
        //与自己前一个同级的课程计划交换orderBy
        //查出当前计划orderBy
        Teachplan teachplan = getById(teachplanId);
        Integer orderby = teachplan.getOrderby();
        if (orderby==1){//顶端不能再进行移动
            throw new XueChengPlusException(ResultEnum.PLAN_MOVEUP_ERROR);
        }
        //与其交换orderBy的同级的课程计划的orderBy值为orderby-1
        //寻找到该同级的课程计划记录
        Teachplan targetTeachplan = lambdaQuery()
                .eq(Teachplan::getCourseId,teachplan.getCourseId())//这个不要忘了，因为teachplan表中有其他课程的记录
                .eq(Teachplan::getParentid, teachplan.getParentid())
                .eq(Teachplan::getOrderby, orderby - 1).one();

        //交换orderby字段
        teachplan.setOrderby(targetTeachplan.getOrderby());
        targetTeachplan.setOrderby(orderby);
        //更新orderBy字段值
        updateById(targetTeachplan);
        updateById(teachplan);


    }

    /**
     * 课程计划下移
     * @param teachplanId
     */
    @Override
    public void movedown(Long teachplanId) {
        Teachplan teachplan = getById(teachplanId);
        Integer orderby = teachplan.getOrderby();//当前计划的orderby值

        //查询出同级最大的orderby，进行比较
        Integer maxOrderby = lambdaQuery().eq(Teachplan::getCourseId, teachplan.getCourseId())
                .eq(Teachplan::getParentid, teachplan.getParentid()).count();

        if (Objects.equals(orderby, maxOrderby)){//底端不能再进行移动
            throw new XueChengPlusException(ResultEnum.PLAN_MOVEDOWN_ERROR);
        }
        //与其交换orderBy的同级的课程计划的orderBy值为orderby+1
        //寻找值为orderby+1的同级的课程计划记录
        Teachplan targetTeachplan = lambdaQuery()
                .eq(Teachplan::getCourseId,teachplan.getCourseId())//这个不要忘了，因为teachplan表中有其他课程的记录
                .eq(Teachplan::getParentid, teachplan.getParentid())
                .eq(Teachplan::getOrderby, orderby + 1).one();

        //交换orderby字段
        teachplan.setOrderby(targetTeachplan.getOrderby());
        targetTeachplan.setOrderby(orderby);
        //更新orderBy字段值
        updateById(targetTeachplan);
        updateById(teachplan);

    }

    /**
     * 递归查询子节点
     * @param rootId  根节点id
     * @param all   所有节点
     * @return 根节点信息
     */
    private List<TeachPlanTreeVo> getChildren(Long rootId, List<TeachPlanTreeVo> all) {
        List<TeachPlanTreeVo> children = all.stream().filter(m -> {
            return Objects.equals(m.getParentid(), rootId);
        }).map(
                (m) -> {
                    List<TeachPlanTreeVo> children1 = getChildren(m.getId(), all);
                    if (!CollectionUtils.isEmpty(children1)){
                        m.setTeachPlanTreeNodes(children1);
                    }
//                    m.setTeachPlanTreeNodes(getChildren(m.getId(), all));
                    return m;
                }
        ).collect(Collectors.toList());
        return children; //返回的这个集合就是最终我们想要的树形结构集合了
    }

}
